﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Security.Cryptography;
using System.Net.Sockets;
using System.Net;

namespace Walson.Net.Sockets
{
    internal class TcpCommon
    {
        private static readonly int _blockLength = 500 * 1024;

        /// <summary>
        /// 计算文件的hash值 
        /// </summary>
        internal string CalcFileHash(string FilePath)
        {
            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
            byte[] hash;
            using (FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096))
            {
                hash = md5.ComputeHash(fs);
            }
            return BitConverter.ToString(hash);
        }

        /// <summary>
        /// 发送文件
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="stream"></param>
        /// <returns></returns>
        internal bool SendFile(string filePath, NetworkStream stream)
        {
            FileStream fs = File.Open(filePath, FileMode.Open);
            int readLength = 0;
            byte[] data = new byte[_blockLength];

            //发送大小
            byte[] length = new byte[8];
            BitConverter.GetBytes(new FileInfo(filePath).Length).CopyTo(length, 0);
            stream.Write(length, 0, 8);

            //发送文件
            while ((readLength = fs.Read(data, 0, _blockLength)) > 0)
            {
                stream.Write(data, 0, readLength);
            }
            fs.Close();
            return true;
        }

        /// <summary>
        /// 接收文件
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="stream"></param>
        /// <returns></returns>
        internal bool ReceiveFile(string filePath, NetworkStream stream)
        {
            try
            {
                long count = GetSize(stream);
                if (count == 0)
                {
                    return false;
                }

                long index = 0;
                byte[] clientData = new byte[_blockLength];
                if (File.Exists(filePath))
                {
                    File.Delete(filePath);
                }
                string path = new FileInfo(filePath).Directory.FullName;
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }

                FileStream fs = File.Open(filePath, FileMode.OpenOrCreate);
                try
                {
                    //计算当前要读取的块的大小
                    int currentBlockLength = 0;
                    if (_blockLength < count - index)
                    {
                        currentBlockLength = _blockLength;
                    }
                    else
                    {
                        currentBlockLength = (int)(count - index);
                    }

                    int receivedBytesLen = stream.Read(clientData, 0, currentBlockLength);
                    index += receivedBytesLen;
                    fs.Write(clientData, 0, receivedBytesLen);

                    while (receivedBytesLen > 0 && index < count)
                    {
                        clientData = new byte[_blockLength];
                        receivedBytesLen = 0;

                        if (_blockLength < count - index)
                        {
                            currentBlockLength = _blockLength;
                        }
                        else
                        {
                            currentBlockLength = (int)(count - index);
                        }
                        receivedBytesLen = stream.Read(clientData, 0, currentBlockLength);
                        index += receivedBytesLen;
                        fs.Write(clientData, 0, receivedBytesLen);
                    }
                }
                catch (Exception ex)
                {
                    return false;
                }
                finally
                {
                    fs.Close();
                }
            }
            catch (Exception ex)
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="message"></param>
        /// <param name="stream"></param>
        /// <returns></returns>
        internal bool SendMessage(string message, NetworkStream stream)
        {

            byte[] data = Encoding.UTF8.GetBytes(message);
            byte[] resultData = new byte[8 + data.Length];
            BitConverter.GetBytes(data.Length).CopyTo(resultData, 0);
            data.CopyTo(resultData, 8);

            stream.Write(resultData, 0, resultData.Length);
            return true;
        }

        /// <summary>
        /// 读取消息
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        internal string ReadMessage(NetworkStream stream)
        {
            string result = "";
            int messageLength = 0;

            byte[] resultbyte = new byte[500 * 1024];
            //读取数据大小
            int index = 0;
            int count = GetSize(stream);

            byte[] data = new byte[count];
            while (index < count && (messageLength = stream.Read(data, 0, count - index)) != 0)
            {
                data.CopyTo(resultbyte, index);
                index += messageLength;
            }
            result = Encoding.UTF8.GetString(resultbyte, 0, index);
            return result;
        }

    
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="buffer">发送缓冲区</param>
        /// <param name="len">发送数据的长度</param>
        /// <param name="stream">所使用的网络流</param>
        /// <returns>发送结果</returns>
        internal bool Send(byte[] buffer, int len, NetworkStream stream)
        {
            byte[] resultData = new byte[8 + len];
            BitConverter.GetBytes(len).CopyTo(resultData, 0);
            buffer.CopyTo(resultData, 8);

            stream.Write(resultData, 0, resultData.Length);
            return true;
        }

        /// <summary>
        /// 读取数据
        /// </summary>
        /// <param name="stream">所使用的网络流</param>
        /// <returns></returns>
        internal byte[] Read(NetworkStream stream)
        {
            int messageLength = 0;

            byte[] resultbyte;
            //读取数据大小
            int index = 0;
            int count = GetSize(stream);
            resultbyte = new byte[count];

            byte[] data = new byte[count];
            while (index < count && (messageLength = stream.Read(data, 0, count - index)) != 0)
            {
                data.CopyTo(resultbyte, index);
                index += messageLength;
            }
            return resultbyte;
        }

        

        /// <summary>
        /// 获取要读取的数据的大小
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        private int GetSize(NetworkStream stream)
        {
            int count = 0;
            byte[] countBytes = new byte[8];
            try
            {
                if (stream.Read(countBytes, 0, 8) == 8)
                {
                    count = BitConverter.ToInt32(countBytes, 0);
                }
                else
                {
                    return 0;
                }
            }
            catch (Exception ex)
            {

            }
            return count;
        }
    }

    /// <summary>
    /// TCP 监听类
    /// </summary>
    public class TcpListenerHelper
    {
        private string _strServerIP = "";
        private int _serverPort = 0;

        TcpListener server;
        TcpClient client;
        NetworkStream netstream;
        IAsyncResult asyncResult;
        TcpCommon tcpCommon = new TcpCommon();

        ManualResetEvent listenConnected = new ManualResetEvent(false);

        bool _active = false;

        public TcpListenerHelper(string strServerIP, int serverPort)
        {
            _strServerIP = strServerIP;
            _serverPort = serverPort;
            server = new TcpListener(IPAddress.Parse(strServerIP), serverPort);
            server.Server.ReceiveTimeout = 6000;
            server.Server.SendTimeout = 6000;
        }

        /// <summary>
        /// 启动
        /// </summary>
        public void Start()
        {
            try
            {
                _active = true;
                server.Start();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            try
            {
                _active = false;
                if (client != null)
                {
                    client.Close();
                }

                if (netstream != null)
                {
                    netstream.Close();
                }
                server.Stop();

            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public void Listen()
        {
            listenConnected.Reset();
            asyncResult = server.BeginAcceptTcpClient(new AsyncCallback(AsyncCall), server);
        }

        public void AsyncCall(IAsyncResult ar)
        {
            try
            {
                TcpListener tlistener = (TcpListener)ar.AsyncState;

                if (_active)
                {
                    client = tlistener.EndAcceptTcpClient(ar);
                    netstream = client.GetStream();
                }
                else
                {
                    client = null;
                    netstream = null;
                }
                listenConnected.Set();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        public bool WaitForConnect()
        {
            listenConnected.WaitOne();

            if (client != null && netstream != null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }


        #region TcpCommon所有方法
        /// <summary>
        /// 计算文件的hash值 
        /// </summary>
        public string CalcFileHash(string FilePath)
        {
            return tcpCommon.CalcFileHash(FilePath);
        }

        /// <summary>
        /// 发送文件
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public bool SendFile(string filePath)
        {
            return tcpCommon.SendFile(filePath, netstream);
        }

        /// <summary>
        /// 接收文件
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public bool ReceiveFile(string filePath)
        {
            return tcpCommon.ReceiveFile(filePath, netstream);
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public bool SendMessage(string message)
        {
            return tcpCommon.SendMessage(message, netstream);
        }

        /// <summary>
        /// 接收消息
        /// </summary>
        /// <returns></returns>
        public string ReadMessage()
        {
            return tcpCommon.ReadMessage(netstream);
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="buffer">发送缓冲区</param>
        /// <param name="len">数据长度</param>
        /// <returns>是否发送成功</returns>
        public bool Send(byte[] buffer, int len)
        {
            return tcpCommon.Send(buffer, len, netstream);
        }

        /// <summary>
        /// 接收数据
        /// </summary>
        /// <returns>接收到的字节数组</returns>
        public byte[] Receive()
        {
            return tcpCommon.Read(netstream);
        }



        #endregion

        #region IDisposable 成员

        public void Dispose()
        {
            Stop();
        }

        #endregion
    }

    /// <summary>
    /// TCP 客户端类
    /// </summary>
    public class TcpClientHelper : IDisposable
    {
        TcpClient client;
        NetworkStream netstream;
        string _serverip = "127.0.0.1";
        int _port = 8080;

        TcpCommon tcpCommon = new TcpCommon();

        #region TcpClientHelper constructor
        public TcpClientHelper(string strServerIP, int serverPort)
        {
            _serverip = strServerIP;
            _port = serverPort;

        }
        #endregion

        public void Start()
        {
            client = new TcpClient(_serverip, _port);
            netstream = client.GetStream();
        }

        public void Stop()
        {
            if (netstream != null)
            {
                netstream.Close();
            }

            if (client != null)
            {
                client.Close();
            }
        }

        #region TcpCommon所有方法
        public string CalcFileHash(string FilePath)
        {
            return tcpCommon.CalcFileHash(FilePath);
        }

        public bool SendFile(string filePath)
        {
            return tcpCommon.SendFile(filePath, netstream);
        }


        public bool ReceiveFile(string filePath)
        {
            return tcpCommon.ReceiveFile(filePath, netstream);
        }


        public bool SendMessage(string message)
        {
            return tcpCommon.SendMessage(message, netstream);
        }

        public string ReadMessage()
        {
            return tcpCommon.ReadMessage(netstream);
        }
        #endregion

        #region IDisposable 成员

        public void Dispose()
        {
            if (netstream != null)
            {
                netstream.Close();
            }

            if (client != null)
            {
                client.Close();
            }
        }

        #endregion
    }
}
